package com.itbaizhan.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.itbaizhan.api.UserAccountBank02Service;
import com.itbaizhan.dto.UserAccountDTO;
import com.itbaizhan.entity.CancelLog;
import com.itbaizhan.entity.ConfirmLog;
import com.itbaizhan.entity.TryLog;
import com.itbaizhan.entity.UserAccount;
import com.itbaizhan.mapper.CancelLogMapper;
import com.itbaizhan.mapper.ConfirmLogMapper;
import com.itbaizhan.mapper.TryLogMapper;
import com.itbaizhan.mapper.UserAccountMapper;
import com.itbaizhan.service.IUserAccountService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.hmily.annotation.HmilyTCC;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;

/**
 * <p>
 * 账户信息 服务实现类
 * </p>
 *
 * @author itbaizhan
 * @since 07-08
 */
@Slf4j
@Service
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements IUserAccountService {

    @DubboReference(version = "1.0.0")
    private UserAccountBank02Service userAccountBank02Service;

    @Resource
    private TryLogMapper tryLogMapper;
    @Resource
    private ConfirmLogMapper confirmLogMapper;
    @Resource
    private CancelLogMapper cancelLogMapper;

    /**
     * 跨库转账  这时间是转出
     * @param userAccountDTO
     */
   // @Transactional(rollbackFor = Exception.class)  注意使用HmilyTCC 不可以用@Transactional，否则会出现数据库存的数据错误,余额不变，转账金额变负数
    @HmilyTCC(confirmMethod = "sayConfrim", cancelMethod = "sayCancel")
    @Override
    public void transferAmountToBank02(UserAccountDTO userAccountDTO) {

        String txNo = userAccountDTO.getTxNo(); //在controllerUUID生成
        log.info("****** 执行bank01 的 Try方法，事务id={}",txNo);

        //1，幂等处理
        TryLog tryLog = tryLogMapper.selectById(txNo);
        if(tryLog != null){
            return;
        }
        //2, 悬挂处理
        if(confirmLogMapper.selectById(txNo) != null || cancelLogMapper.selectById(txNo) != null){
            return;
        }

        //1.根据账户编号查询账户信息,这里可以用baseMapper,
        UserAccount userAccount = baseMapper.selectById(userAccountDTO.getSourceAccountNo());

        //2判断账户是否存在
        if(userAccount != null){
            //更新账户
            LambdaUpdateWrapper<UserAccount> ulw = new LambdaUpdateWrapper<>();
            //更新转账金额
            ulw.set(UserAccount::getTransferAmount,userAccount.getTransferAmount().add(userAccountDTO.getBigDecimal()));
            // 更新余额
            ulw.set(UserAccount::getAccountBalance,userAccount.getAccountBalance().subtract(userAccountDTO.getBigDecimal()));

            ulw.eq(UserAccount::getAccountNo,userAccountDTO.getSourceAccountNo());
            //ulw是查询条件
            baseMapper.update(null,ulw);


        }

        //3. 准备阶段记录
        TryLog tryLog1 = new TryLog();
        tryLog1.setTxNo(txNo);
        tryLog1.setCreateTime(LocalDateTime.now());
        tryLogMapper.insert(tryLog1);

        //4远程调用  转入微服   跨库转账的功能
        userAccountBank02Service.transferAmountToBank02(userAccountDTO);

    }

    /**
     * 确认阶段
     * @param userAccountDTO
     * @HmilyTCC(confirmMethod = "sayConfrim", cancelMethod = "sayCancel")    方法名必须与里面的参数一样
     */
    public void sayConfrim(UserAccountDTO userAccountDTO){
        String txNo = userAccountDTO.getTxNo(); //在controllerUUID生成
        log.info("****** 执行bank01 的 confrim方法，事务id={}",txNo);

        /*这里是转出账户
        * 确认阶段就是把表里的要转账金额的字段，修改为原来的多小*/
        //1，幂等处理
        ConfirmLog confirmLog = confirmLogMapper.selectById(txNo);
        //如果查询到，说明已运行了一次不用再重复运行
        if(confirmLog != null){
            return;
        }
        //2更新操作
        //2.1 根据id查询账户
        UserAccount userAccount = baseMapper.selectById(userAccountDTO.getSourceAccountNo());
        //修改转账金额
        userAccount.setTransferAmount(userAccount.getTransferAmount().subtract(userAccountDTO.getBigDecimal()));
        baseMapper.updateById(userAccount);

        //3,确认日志记录
        ConfirmLog confirmLog1 = new ConfirmLog();
        confirmLog1.setTxNo(txNo);
        confirmLog1.setCreateTime(LocalDateTime.now());
        confirmLogMapper.insert(confirmLog1);
    }

    /**
     * 取消阶段
     * @param userAccountDTO
     * @HmilyTCC(confirmMethod = "sayConfrim", cancelMethod = "sayCancel")    方法名必须与里面的参数一样
     */
    public void sayCancel(UserAccountDTO userAccountDTO){
        String txNo = userAccountDTO.getTxNo();
        log.info("****** 执行bank01 的 cancel方法，事务id={}",txNo);
        //1，幂等处理
        CancelLog cancelLog = cancelLogMapper.selectById(txNo);
        if(cancelLog != null){
            return;
        }
        //2更新操作
        //2.1 根据id查询账户
        UserAccount userAccount = baseMapper.selectById(userAccountDTO.getSourceAccountNo());
        //修改账户余额,将转出去的加回来，修改回原来的金额
        userAccount.setAccountBalance(userAccount.getAccountBalance().add(userAccountDTO.getBigDecimal()));
        //修改转账金额，减去转出的金额变回0
        userAccount.setTransferAmount(userAccount.getTransferAmount().subtract(userAccountDTO.getBigDecimal()));
        baseMapper.updateById(userAccount);

        //3,记录日志
        CancelLog cancelLog1 = new CancelLog();
        cancelLog1.setTxNo(txNo);
        cancelLog1.setCreateTime(LocalDateTime.now());
        cancelLogMapper.insert(cancelLog1);

    }
}

/*
入github.com  搜索hmily 进入官网查看 ，点击 快速开始 ，用户文档
Dubbo实现项目
步骤一 ： 引入依赖hmily的jar包

步骤二 ： 新增Hmily配置

步骤三 ： 在实现方法上添加注解。TCC模式，则需要完成 confirm，cancel方法的开发*/
/*confirmMethod : 确认方法名称，该方法参数列表与返回类型应与标识方法一致。

cancelMethod : 回滚方法名称，该方法参数列表与返回类型应与标识方法一致。

TCC模式应该保证 confirm 和 cancel 方法的幂等性，用户需要自行去开发这个2个方法，
所有的事务的确认与回滚，完全由用户决定。Hmily框架只是负责来进行调用*/